package org.jet.emall.config;


import lombok.Data;
import org.jet.emall.rbac.service.CustomAdminUrlPrivilegeRoleViewService;
import org.jet.emall.security.comp.DynamicPrivilegeManager;
import org.jet.emall.security.comp.DynamicPrivilegeManagerImpl;
import org.jet.emall.security.filter.LoginFilter;
import org.jet.emall.security.filter.TokenAuthenticationFilter;
import org.jet.emall.security.handler.*;
import org.jet.emall.security.token.JwtUtils;
import org.jet.emall.security.vote.RestfulUrlVote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.util.Arrays;

/**
 * @author xiaozai
 * @version 1.0
 * @date 2020-04-06 19:44
 *
 */
@Configuration
@EnableWebSecurity
@Data
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private CustomAdminUrlPrivilegeRoleViewService customAdminUrlPrivilegeRoleViewService;

    @Autowired
    private WhiteListConfig whiteListConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();


        //所有options请求放行，这是复杂请求的预检操作
        registry.antMatchers(HttpMethod.OPTIONS).permitAll();


        //配置白名单
        whiteListConfig.getWhiteList().forEach(url->registry.antMatchers(url,"*").permitAll());


        //放行登录请求
        registry.antMatchers(HttpMethod.POST,"/login").permitAll();

        //所有其它请求都要验证
        registry.anyRequest().authenticated();

        //关闭csrf,否则不能用postman测试我们的接口
        http.csrf().disable();

        //禁用session,我们先不用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);


        //重点是这句，开启跨域支持
        http.cors();

        //添加我们自己的登录过滤器,替换掉默认的登录过滤器
        http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        //添加我们的token验证过滤器
        http.addFilterBefore(tokenAuthenticationFilter(), LogoutFilter.class);
        //配置认证用户/匿名用户权限不够时的回调处理器
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).accessDeniedHandler(accessDeniedHandler());
        //配置决策管理器
        registry.accessDecisionManager(accessDecisionManager());

        //配置注销url和注销成功处理器
        http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler());

    }


    @Bean
    public LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setFilterProcessesUrl("/login");
        loginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
        loginFilter.setAuthenticationManager(authenticationManager());

        return loginFilter;
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        AuthenticationSuccessHandlerImpl authenticationSuccessHandler = new AuthenticationSuccessHandlerImpl();
        authenticationSuccessHandler.setJwtUtils(jwtUtils());
        return authenticationSuccessHandler;
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        AuthenticationFailureHandlerImpl authenticationFailureHandler = new AuthenticationFailureHandlerImpl();
        return authenticationFailureHandler;
    }


    @Bean
    public AccessDeniedHandler accessDeniedHandler(){
        return new AccessDeniedHandlerImpl();
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint(){
        return new AuthenticationEntryPointImpl();
    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtUtils jwtUtils(){
        JwtUtils jwtUtils = new JwtUtils();
        jwtUtils.setIssuer(jwtConfig.getIssuer());
        jwtUtils.setSeconds(jwtConfig.getSeconds());
        jwtUtils.setSecret(jwtConfig.getSecret());
        jwtUtils.setTokenHead(jwtConfig.getTokenHead());
        jwtUtils.setTokenHeaderName(jwtConfig.getTokenHeaderName());
        System.out.println("issuer="+jwtConfig.getIssuer());
        return jwtUtils;
    }

    @Bean
    public DynamicPrivilegeManager dynamicPrivilegeManager(){
        DynamicPrivilegeManagerImpl dynamicPrivilegeManager = new DynamicPrivilegeManagerImpl();
        dynamicPrivilegeManager.setCustomAdminUrlPrivilegeRoleViewService(customAdminUrlPrivilegeRoleViewService);
        return dynamicPrivilegeManager;
    }

    @Bean
    public RestfulUrlVote restfulUrlVote(){
        RestfulUrlVote restfulUrlVote = new RestfulUrlVote();
        //一定要配置好动态权限管理器
        restfulUrlVote.setDynamicPrivilegeManager(dynamicPrivilegeManager());
        return restfulUrlVote;
    }

    @Bean
    public AccessDecisionManager accessDecisionManager(){
        //我们采用一票通过决策管理器
        AffirmativeBased affirmativeBased = new AffirmativeBased(Arrays.asList(restfulUrlVote(),new RoleVoter(),new AuthenticatedVoter()));
        return affirmativeBased;
    }

    @Bean
    public TokenAuthenticationFilter tokenAuthenticationFilter(){
        TokenAuthenticationFilter tokenAuthenticationFilter = new TokenAuthenticationFilter();
        tokenAuthenticationFilter.setJwtUtils(jwtUtils());
        return tokenAuthenticationFilter;

    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return new LogoutSuccessHandlerImpl();
    }
}
